import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Scanner;
import java.util.Stack;

/**
 * 
 * @author Ye Zhefan
 * 
 */
public class CScanner {

	private static ArrayList<ArrayList<String>> scopeList = new ArrayList<ArrayList<String>>();
	private static ArrayList<List> idScopeList = new ArrayList<List>();

	private static ArrayList<String> identifiers = new ArrayList<String>();
	public static String[] begin = { "A", "B", "C", "D", "E", "F", "G", "H",
			"I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U",
			"V", "W", "X", "Y", "Z", "a", "b", "c", "d", "e", "f", "g", "h",
			"i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u",
			"v", "w", "x", "y", "z", "_" };
	public static char[] rest = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I',
			'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
			'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i',
			'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
			'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8',
			'9' };
	public static char[] total = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I',
			'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
			'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i',
			'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
			'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8',
			'9', '_', '*', '{', '}' };
	public static String[] datatype = { "int", "float", "double", "char",
			"void", "enum", "boolean", "short", "long", "signed", "unsigned",
			"const", "volatile", "main", "struct", "extern", "sizeof",
			"typedef", "for", "if", "break", "else", "case", "while", "return",
			"goto", "do", "switch", "continue", "default", "static" };

	public static void main(String[] args) {
		// Location of file to read
		File file = new File("Gcc_Preprocessed.c");
		try {
			Scanner scanner = new Scanner(file);
			while (scanner.hasNextLine()) {
				String line = scanner.nextLine();
				if (!line.startsWith("#")) {
					String chunk = "";
					for (int u = 0; u < line.length(); u++) {
						boolean added = false;
						if (exist(line.charAt(u))) {
							chunk += line.charAt(u);
							added = true;
						} else if (!added && chunk.length() > 0) {
							addID(chunk);
							chunk = "";
						}
					}
				}
			}
			scanner.close();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
		// System.out.println(identifiers);
		List idCounter = new List();
		for (int i = 0; i < identifiers.size(); i++) {
			Node n = new Node(identifiers.get(i), 1);
			if (idCounter.containElement(identifiers.get(i), idCounter)) {
				idCounter.get(idCounter.index(n.getElement(), idCounter))
						.setCount(
								(idCounter.get(
										idCounter.index(n.getElement(),
												idCounter)).getCount() + 1));
			} else {
				idCounter.add(n);
			}
		}

		// for (int i = 0; i < idCounter.size(); i++) {
		// System.out.println(idCounter.get(i).getElement() + " "
		// + idCounter.get(i).getCount());
		// }

		/**
		 * find top10
		 */
		System.out
				.println("The top 10 most frequently used identifier names: ");
		for (int i = 0; i < 10; i++) {
			if (idCounter.size() > 10) {
				Node biggest = findBiggest(idCounter);
				System.out.println(i + 1 + ". " + biggest.getElement() + " "
						+ biggest.getCount());

				idCounter.remove(idCounter.findElement(biggest.getElement(),
						idCounter));
			}
		}

		/**
		 * find pair
		 */
		PairList pl = makePair(identifiers);
		int[] pairIndex = new int[pl.size()];
		for (int i = 0; i < pairIndex.length; i++) {
			pairIndex[i] = 0;
		}
		for (int i = 0; i < pl.size(); i++) {
			for (int j = i; j < pl.size(); j++) {
				if (pl.get(i).getA().equals(pl.get(j).getA())
						&& pl.get(i).getB().equals(pl.get(j).getB())) {
					pairIndex[i]++;
				}
			}
		}

		System.out.println("");
		System.out.println("The most frequent ordered pair: "
				+ pl.get(maxPair(pairIndex)).getA() + " - "
				+ pl.get(maxPair(pairIndex)).getB());

		/**
		 * find least 10
		 */
		System.out
				.println("\nThe top 10 least frequently used identifier names: ");
		for (int i = 0; i < 10; i++) {
			if (idCounter.size() > 10) {
				Node least = findLeast(idCounter);
				System.out.println(i + 1 + ". " + least.getElement() + " "
						+ least.getCount());

				idCounter.remove(idCounter.findElement(least.getElement(),
						idCounter));
			}
		}

		/**
		 * scope
		 */
		scope(file);
	}

	private static void scope(File file) {
		Stack<Character> par = new Stack<Character>();
		try {
			Scanner scanner = new Scanner(file);
			while (scanner.hasNextLine()) {
				String line = scanner.nextLine();
				if (!line.startsWith("#")) {
					String chunk = "";
					int index = 0;
					for (int u = 0; u < line.length(); u++) {
						boolean added = false;
						if (line.charAt(u) == '{') {
							index++;
							par.push('{');
						} else if (line.charAt(u) == '}') {
							par.pop();
							index--;
						}
						if (!par.isEmpty()) {
							if ((exist(line.charAt(u)))) {
								chunk += line.charAt(u);
								added = true;
							} else if (!added && chunk.length() > 0) {
								addScopeList(chunk, index);
								chunk = "";
							}
						}
					}
				}
			}
			scanner.close();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}

		for (int i = 0; i < scopeList.size(); i++) {
			sortScope(i);
		}

		int[] dist = new int[idScopeList.size()];
		for (int i = 0; i < dist.length; i++) {
			dist[i] = 0;
		}
		for (int i = 0; i < idScopeList.size(); i++) {
			for (int j = 0; j < idScopeList.get(i).size(); j++) {
				if (idScopeList.get(i).get(j).getCount() == 1) {
					dist[i]++;
				}
			}
		}

		int max = 0;
		int index = 0;
		for (int i = 0; i < dist.length; i++) {
			if (max < dist[i]) {
				max = dist[i];
				index = i;
			}
		}
		System.out.println(max);
	}

	private static void addScopeList(String token, int index) {
		if ((token.charAt(token.length() - 1) == ';'
				| token.charAt(token.length() - 1) == ')'
				| token.charAt(token.length() - 1) == ','
				| token.charAt(token.length() - 1) == ':' | token.charAt(token
				.length() - 1) == ']')
				&& token.length() > 1) {
			token = token.substring(0, token.length() - 1);
		}
		if ((token.charAt(0) == '(' | token.charAt(0) == '[')
				&& token.length() > 1) {
			token = token.substring(1);
		}

		int[] count = new int[token.length() - 1];
		for (int x = 0; x < count.length; x++) {
			count[x] = 0;
		}
		for (int i = 0; i < begin.length; i++) {
			if (token.startsWith(begin[i])) {
				for (int j = 1; j < token.length(); j++) {
					for (int k = 0; k < rest.length; k++) {
						if (token.charAt(j) == rest[k]) {
							count[j - 1] = 1;
						}
					}
				}
				int check = 1;
				for (int t = 0; t < count.length; t++) {
					if (count[t] == 0) {
						check = 0;
					}
				}
				if (check == 1 && !isDatatype(token)) {
					scopeList.get(index).add(token);
				}
			}
		}
	}

	private static void sortScope(int index) {
		List idCounter = new List();
		for (int i = 0; i < scopeList.get(index).size(); i++) {
			Node n = new Node(scopeList.get(index).get(i), 1);
			if (idCounter
					.containElement(scopeList.get(index).get(i), idCounter)) {
				idCounter.get(idCounter.index(n.getElement(), idCounter))
						.setCount(
								(idCounter.get(
										idCounter.index(n.getElement(),
												idCounter)).getCount() + 1));
			} else {
				idCounter.add(n);
			}
		}
		idScopeList.add(idCounter);
	}

	private static int maxPair(int[] temp) {
		int index = 0;
		int num = 0;
		for (int i = 0; i < temp.length; i++) {
			if (num < temp[i]) {
				num = temp[i];
				index = i;
			}
		}
		return index;
	}

	private static PairList makePair(ArrayList<String> origin) {
		PairList list = new PairList();
		for (int i = 0; i < origin.size() - 1; i++) {
			PairNode pn = new PairNode();
			pn.setA(origin.get(i));
			pn.setB(origin.get(i + 1));
			list.add(pn);
		}
		return list;
	}

	private static Node findBiggest(List temp) {
		Node ret = new Node();
		ret.setCount(0);
		for (int i = 0; i < temp.size(); i++) {
			if (ret.getCount() < temp.get(i).getCount()) {
				ret.setCount(temp.get(i).getCount());
				ret.setElement(temp.get(i).getElement());
			}
		}
		return ret;
	}

	private static Node findLeast(List temp) {
		Node ret = new Node();
		ret.setCount(10000);
		for (int i = 0; i < temp.size(); i++) {
			if (ret.getCount() > temp.get(i).getCount()) {
				ret.setCount(temp.get(i).getCount());
				ret.setElement(temp.get(i).getElement());
			}
		}
		return ret;
	}

	private static void addID(String token) {
		if ((token.charAt(token.length() - 1) == ';'
				| token.charAt(token.length() - 1) == ')'
				| token.charAt(token.length() - 1) == ','
				| token.charAt(token.length() - 1) == ':' | token.charAt(token
				.length() - 1) == ']')
				&& token.length() > 1) {
			token = token.substring(0, token.length() - 1);
		}
		if ((token.charAt(0) == '(' | token.charAt(0) == '[')
				&& token.length() > 1) {
			token = token.substring(1);
		}

		int[] count = new int[token.length() - 1];
		for (int x = 0; x < count.length; x++) {
			count[x] = 0;
		}
		for (int i = 0; i < begin.length; i++) {
			if (token.startsWith(begin[i])) {
				for (int j = 1; j < token.length(); j++) {
					for (int k = 0; k < rest.length; k++) {
						if (token.charAt(j) == rest[k]) {
							count[j - 1] = 1;
						}
					}
				}
				int check = 1;
				for (int t = 0; t < count.length; t++) {
					if (count[t] == 0) {
						check = 0;
					}
				}
				if (check == 1 && !isDatatype(token)) {
					identifiers.add(token);
				}
			}
		}
	}

	private static boolean exist(char a) {
		boolean ret = false;
		for (int i = 0; i < total.length; i++) {
			if (a == total[i]) {
				ret = true;
			}
		}
		return ret;
	}

	private static boolean isDatatype(String check) {
		boolean ret = false;
		for (int i = 0; i < datatype.length; i++) {
			if (check.equals(datatype[i])) {
				return true;
			}
		}
		return ret;
	}
}
